package space

import (
	"errors"
	"reflect"
	"src/protoMsg"
	"src/zeus/entity"
	"src/zeus/iserver"
	"src/zeus/linmath"
	"src/zeus/msgdef"
	"time"
)

/*
	TinyEntity 轻量级的Entity 在场景中可以大量放置的Entity
	只有最简单的标志自己位置相关的能力
	所有的属性必须在后代的 Init函数中完成
	没有独立的Loop相关方法，属性也无法改变

	//modify by zgb 2019.07.26
	PS 这里已经修改了,现在这里用来做NPC的基类，需要独立的Loop，用来执行AI，AOI同步等功能。
*/

// ITinyEntity TinyEntity接口
type ITinyEntity interface {
	iserver.ISpaceEntity
	entity.IPropsContainer
	// GetTinyEntityDef() *entitydef.TinyEntityDef
	GetPropDef(entityType string) *entity.Def
	// 设置同步属性
	SetSync()
}

type iTinyEntity interface {
	onInit() bool
	onLoop(param ...interface{})
	onDestroy()

	onEntityCreated(entityID uint64, entityType string, space iserver.ISpace, initParam interface{}, realPtr ITinyEntity) error
}

type iTinyEntityLoopState interface {
	NeedLoop() bool
}

// iEntityState 留给后代作一些回调方法
type iEntityInit interface {
	Init(initParam interface{}) bool
}

type iEntityLoop interface {
	Loop()
}

type iEntityDestroy interface {
	Destroy()
}

var _ iTinyEntity = &TinyEntity{}
var _ iserver.ICoordEntity = &TinyEntity{}

// 最多这么多次Loop就进行一次同步
const MaxSyncNum = 30

// TinyEntity 轻量级的Entity
type TinyEntity struct {
	*entity.PropsSyncContainer
	// entitydef.TinyEntityDef

	Position *entity.PropInfo // Position 是 IPropsContainer 内数据 的指针

	id        uint64
	typ       string
	realPtr   ITinyEntity
	space     iserver.ISpace
	initParam interface{}

	//是否需要更新九宫格
	NeedUpdateCoord bool
	//观察列表
	extWatchList map[uint64]*extWatchEntity

	/*
		每帧清理一次，所以需要注意调用的频率，
		正常来说 获取的频率也应该是每帧获取
		（这里已经不能保证了，因为本实体的同步频率比较低）
	*/
	DirtyClientMsg *protoMsg.PropBaseSyncList
	// 还剩多少次操作才会同步
	SyncNum int
	//不同的Tiny对象的Loop时间会不一样，保持1秒一次的同步
	MaxSyncNum int

	aoiIndex     uint8   // AOI层级(MMO游戏只需要1层，所以默认是0)
	needHide     bool    // 是否需要隐藏
	hide         bool    // 是否已经隐藏
	DestroyFlag  bool    // 删除标记
	LoopInterval int32   // Loop的配置间隔，只有需要做loop的实体才有用
	LoopTimerID  uint64  // Loop定时器ID
	LastLoopTime int64   // 上次loop的时间
	DeltaTime    float32 // 实际loop的间隔（秒）

	onDestroyFunc func()
	// Loop结束的时候，用于生成广播消息的方法（主要用于技能有关的RPC消息）
	// LoopSendMsgList []func()
}

// 是否加载失败，如果失败，后面的逻辑就不继续下去了
func (this *TinyEntity) GetIsFailLoad() bool {
	return false
}

// 是否加载失败，如果失败，后面的逻辑就不继续下去了
func (this *TinyEntity) SetLoadFail() {
	// this.isFailLoad = true
}

type tinyPropInfo struct {
	typ   reflect.Kind
	value interface{}
}

func (e *TinyEntity) onEntityCreated(entityID uint64, entityType string, space iserver.ISpace, initParam interface{}, realPtr ITinyEntity) error {
	e.id = entityID
	e.typ = entityType
	e.realPtr = realPtr
	e.space = space
	if space == nil {
		return errors.New("TinyEntity onEntityCreated space is nil.")
	}
	e.initParam = initParam
	e.DirtyClientMsg = nil
	e.PropsSyncContainer = entity.NewPropsSyncContainer()
	e.DelDirtyPropSorter(entity.EDPSorterTypeSave)
	// e.dirtyMsgGetSet = make(map[uint64]struct{})
	ps, ok := e.realPtr.(iserver.IEntityPropsSetter)
	if ok {
		ps.SetPropsSetter(e.realPtr.(iserver.IEntityProps))
	}

	// e.IPropsContainer = entity.NewPropsContainer()
	// e.IPropsContainer.AddDirtyPropSorter(entity.EDPSorterTypeSync)

	e.needHide = false
	e.hide = false
	e.DestroyFlag = false
	e.LoopInterval = 500
	e.LoopTimerID = 0
	e.LastLoopTime = 0
	e.DeltaTime = 0
	e.MaxSyncNum = MaxSyncNum
	e.SyncNum = MaxSyncNum
	return nil
}

// 设置同步属性
func (e *TinyEntity) SetSync() {
	// logger.Debugf(logger.LogKey_Prop, "SetSync:%d", e.SyncNum)
	e.SyncNum = 0
}

// func (e *TinyEntity) GetTinyEntityDef() *entitydef.TinyEntityDef {
// 	return &e.TinyEntityDef
// }

// GetID 获取ID号
func (e *TinyEntity) GetID() uint64 {
	return e.id
}

// GetType 获取类型
func (e *TinyEntity) GetType() string {
	return e.typ
}

// GetType 添加分组
func (e *TinyEntity) AddGroup(g string) {
}

// GetType 添加分组
func (e *TinyEntity) DelGroup(g string) {
}

// GetType 获取分组列表
func (e *TinyEntity) GetGroups() interface{} {
	return nil
}

// GetType 获取分组
func (e *TinyEntity) GetGroup(g string) (interface{}, bool) {
	return nil, false
}

// GetRealPtr 获取后代类
func (e *TinyEntity) GetRealPtr() interface{} {
	return e.realPtr
}

// GetSpace 获取空间
func (e *TinyEntity) GetSpace() iserver.ISpace {
	return e.space
}

// 获取半径
func (e *TinyEntity) GetModelRadius() float32 {
	return 0
}

// GetInitParam 获取初始化参数
func (e *TinyEntity) GetInitParam() interface{} {
	return e.initParam
}

var _ entity.IPropDefProvider = &TinyEntity{}

func (e *TinyEntity) GetPropDef(_ string) *entity.Def {
	return entity.GetDefs().GetDef("TinyEntity")
}
func (e *TinyEntity) onInit() bool {
	e.SetAOILayerIndex(0)
	e.realPtr.InitProp2(e.realPtr.GetPropDef(e.typ), e.typ)
	// if ic, ok := e.realPtr.(entity.IPropDefProvider); ok {
	// 	e.InitProp2(ic.GetPropDef(e.typ), e.typ)
	// } else {
	// 	logger.Warn("Entity 没有提供 IPropDefProvider 实现")
	// }
	// e.Position = e.GetPropRaw2(entitydef.TINYENTITY_Prop_Position)
	// if e.Position == nil {
	// 	logger.Error("TinyEntity get Position prop is nil e.typ = ", e.typ)
	// 	return false
	// }
	ii, ok := e.GetRealPtr().(iEntityInit)
	if ok {
		if ii.Init(e.initParam) == false {
			return false
		}
	}
	//e.initState()
	e.space.UpdateCoord(e.GetRealPtr().(iserver.ICoordEntity))
	e.NeedUpdateCoord = false
	e.extWatchList = make(map[uint64]*extWatchEntity)
	if il, ok := e.GetRealPtr().(iTinyEntityLoopState); ok && il.NeedLoop() {
		e.LastLoopTime = e.space.GetCurTime()
		// e.MaxSyncNum = 1
		e.MaxSyncNum = int(1000 / e.LoopInterval)
		e.LoopTimerID = e.space.GetTimer().LoopTimer(time.Duration(e.LoopInterval)*time.Millisecond, e.onLoop)
	}
	return true
}

func (e *TinyEntity) NeedLoop() bool {
	return false
}
func (e *TinyEntity) onLoop(param ...interface{}) {
	if e.GetSpace() == nil {
		return
	}
	e.DeltaTime = float32(e.GetSpace().GetCurTime()-e.LastLoopTime) * 0.001
	ii, ok := e.GetRealPtr().(iEntityLoop)
	if ok {
		ii.Loop()
	}
	if e.DestroyFlag {
		_ = e.DestroyFlag
	}
	if e.NeedUpdateCoord == true {
		e.GetSpace().UpdateCoord(e.GetRealPtr().(iserver.ICoordEntity))
		e.NeedUpdateCoord = false
	}
	e.SyncNum--
	// 处理AOI信息
	// e.DirtyClientMsg = nil
	// e.dirtyMsgGetSet = make(map[uint64]struct{})
	e.LastLoopTime = e.space.GetCurTime()
	// // 设置需要广播的消息
	// li := e.LoopSendMsgList
	// for _, f := range li {
	// 	f()
	// }
	// e.LoopSendMsgList = e.LoopSendMsgList[0:0]
}

func (e *TinyEntity) onDestroy() {
	if e.DestroyFlag {
		//进来过就不再进来了
		return
	}
	e.DestroyFlag = true

	if e.GetSpace() != nil {
		if e.LoopTimerID != 0 {
			e.GetSpace().GetTimer().DeleteTimerTask(e.LoopTimerID)
			e.LoopTimerID = 0
		}
		if e.onDestroyFunc != nil {
			e.onDestroyFunc()
		}
		// 延迟删除，因为可能有逻辑还在执行
		/*
			把自己从管理器中删除的逻辑，
			有可能是自己的某个定时器任务；
			它的运行逻辑可能会在删除的时候，还要继续运行；
			而对象的Destroy方法中，可能有些系统会把自己的属性设置成nil；
			从而导致还在运行的逻辑拿不到属性的情况发生；
			所以，需要延时执行Destroy;
			但从集合中删除是立即执行的；
			所以所有系统如果要从集合中获取目标对象的时候，就会获取不到；
			在写代码的时候，需要注意；
			1、BattleEntity对象在运行的时候，有可能会被其他对象引用，所以它下面的所有属性不能设置为nil；（即使它从集合中被删除）
			2、所有系统都会主动调用RemoveTinyEntity；
		*/
		e.GetSpace().CallDelay(func() {
			if e == nil {
				return
			}
			ii, ok := e.GetRealPtr().(iEntityDestroy)
			if ok {
				ii.Destroy()
			}
			e.GetSpace().RemoveFromCoord(e.GetRealPtr().(iserver.ICoordEntity))
			e.ClearWatcher()

		})
	}

}

func (this *TinyEntity) GetNowTime() time.Time {
	return this.space.GetNowTime()
}

// SetPos 设置位置
func (e *TinyEntity) SetPos(pos linmath.Vector3) {
	// logger.Debug(logger.LogKey_MoveSys, "[TinyEntity] SetPos ", pos)
	tmp := pos
	if !pos.IsEqual(e.GetPos()) && e.Position.SetValue(&tmp) {
		e.NeedUpdateCoord = true
	}
}

// GetPos 获取位置
func (e *TinyEntity) GetPos() linmath.Vector3 {
	protoVec3 := e.Position.GetValue().(*linmath.Vector3)
	// pos := linmath.NewVector3(protoVec3.GetX(), protoVec3.GetY(), protoVec3.GetZ())
	return *protoVec3
}

/*
获取所在的点，避免分配内存，由于是引用注意不要轻易修改内容
*/
func (e *TinyEntity) GetPosRef() *linmath.Vector3 {
	protoVec3 := e.Position.GetValue().(*linmath.Vector3)
	return protoVec3
}

// SetRota 设置旋转
func (e *TinyEntity) SetRota(rota int32) {
	// if rota != e.GetProp(entitydef.TINYENTITY_Prop_Rot) {
	// 	e.SetProp(entitydef.TINYENTITY_Prop_Rot, rota)
	// }

}

// GetRota 获取旋转
func (e *TinyEntity) GetRota() int32 {
	return 0
	// return e.GetProp(entitydef.TINYENTITY_Prop_Rot).(int32)
}

// GetAOIProp 获取属性的打包
func (e *TinyEntity) GetAOIProp() msgdef.IMsg {
	return entity.PackProps(e, entity.PackProp_AOI)
}

// GetAOIProp 获得刷新AOI需要收到的属性数据
func (e *TinyEntity) GetAOIUpdateProp() *protoMsg.PropBaseSyncList {

	/*
		原本会记录获取过的getter数据，来考虑是否需要再生成数据；
		因为需要及时发属性给客户端，有变化的时候，还是要在当前帧生成数据，只要通过
		e.GetSpace().AddIDirty，在帧尾把数据清了就不会重复发送了。
		2024、3、30
		考虑优化性能，AOI的同步频率可以降低；


	*/
	if e.SyncNum > 0 {
		return e.DirtyClientMsg
	} else {
		e.SyncNum = e.MaxSyncNum
	}
	if e.DirtyClientMsg == nil {
		e.PropsSyncContainer.PackPropsSyncProps()
		e.DirtyClientMsg = e.PropsSyncContainer.GetDirtyAOIMsg()
		e.GetSpace().AddIDirty(e.GetRealPtr().(iserver.IDirty))
	}
	return e.DirtyClientMsg
}

// 清空脏数据
func (e *TinyEntity) ClearDirty() {
	// if e.DirtyClientMsg != nil {
	e.DirtyClientMsg = nil
	// }
	// logger.Debug(logger.LogKey_AOI, "DirtyClientMsg = nil")
}

// GetAOILayerIndex 获取AOI的层级
func (e *TinyEntity) GetAOILayerIndex() uint8 {
	return e.aoiIndex
}

// SetAOILayerIndex 设置AOI的层级
func (e *TinyEntity) SetAOILayerIndex(i uint8) {
	e.aoiIndex = i
}

// IsAOITrigger 是否AOI触发
func (e *TinyEntity) IsAOITrigger() bool {
	return false
}

// IsWatcher 不是观察者
func (e *TinyEntity) IsWatcher() bool {
	return false
}

// SetNeedHide 是否需要在AOI中隐藏
func (e *TinyEntity) SetNeedHide(hide bool) {
	e.needHide = hide
}

// GetNeedHide 是否需要在AOI中隐藏
func (e *TinyEntity) GetNeedHide() bool {
	return e.needHide
}

// SetHide 是否已经在AOI中隐藏
func (e *TinyEntity) SetHide(hide bool) {
	e.hide = hide
	e.SetNeedHide(hide)
}

// GetHide 是否已经在AOI中隐藏
func (e *TinyEntity) GetHide() bool {
	return e.hide
}

// 是否对玩家隐藏
func (e *TinyEntity) GetPlayerHide() bool {
	return false
}

// 隐藏别人 针对我(记录在我这边)
func (e *TinyEntity) SetHideOthersToMe(v bool) {

}

// 隐藏自己 针对别人(记录在我这边)
func (e *TinyEntity) SetHideMeToOthers(v bool) {

}

// 获取AOI系统中需要知道的主人ID
func (e *TinyEntity) GetAOIOwnerUID() uint64 {
	return 0
}

// GetKey 获取在maplist中的key
func (e *TinyEntity) GetKey() interface{} {
	return e.GetID()
}

// func (e *TinyEntity) getValueStreamSize(v *tinyPropInfo) int {

// 	s := 0
// 	switch v.typ {
// 	case reflect.Int8, reflect.Uint8:
// 		s = 1
// 	case reflect.Int16, reflect.Uint16:
// 		s = 2
// 	case reflect.Int32, reflect.Uint32:
// 		s = 4
// 	case reflect.Int64, reflect.Uint64:
// 		s = 8
// 	case reflect.Float32:
// 		s = 4
// 	case reflect.Float64:
// 		s = 8
// 	case reflect.String:
// 		s = len(v.value.(string)) + 2
// 	default:
// 		panic("no support type")
// 	}

// 	typeName := v.typ.String()

// 	return s + len(typeName) + 2
// }

// func (e *TinyEntity) writeValueToStream(bs *bytestream.ByteStream, v *tinyPropInfo) error {

// 	var err error

// 	typeName := v.typ.String()
// 	err = bs.WriteStr(typeName)
// 	if err != nil {
// 		return err
// 	}

// 	switch v.typ {
// 	case reflect.Int8:
// 		err = bs.WriteInt8(v.value.(int8))
// 	case reflect.Uint8:
// 		err = bs.WriteByte(v.value.(uint8))
// 	case reflect.Int16:
// 		err = bs.WriteInt16(v.value.(int16))
// 	case reflect.Uint16:
// 		err = bs.WriteUInt16(v.value.(uint16))
// 	case reflect.Int32:
// 		err = bs.WriteInt32(v.value.(int32))
// 	case reflect.Uint32:
// 		err = bs.WriteUInt32(v.value.(uint32))
// 	case reflect.Int64:
// 		err = bs.WriteInt64(v.value.(int64))
// 	case reflect.Uint64:
// 		err = bs.WriteUInt64(v.value.(uint64))
// 	case reflect.String:
// 		err = bs.WriteStr(v.value.(string))
// 	case reflect.Float32:
// 		err = bs.WriteFloat32(v.value.(float32))
// 	case reflect.Float64:
// 		err = bs.WriteFloat64(v.value.(float64))
// 	default:
// 		panic("no support type")
// 	}

// 	return err
// }

// 这个是用在battle包里的IExtEntity引用接口，正确的写法应该写在room里的ntt基类里才对。
func (e *TinyEntity) AddcurWS(ws int32) {
}

// //IAOITriggerEntity 接口实现------------------------------

// //OnEntityEnterAOI 实体进入AOI范围
// func (te *TinyEntity) OnEntityEnterAOI(o iserver.ICoordEntity) {

// }

// //OnEntityLeaveAOI 实体离开AOI范围
// func (te *TinyEntity) OnEntityLeaveAOI(o iserver.ICoordEntity) {

// }

// func (te *TinyEntity) OnAfterEntityEnterAOI(e iserver.ICoordEntity) {

// }

// 插入观察者
func (e *TinyEntity) AddExtWatchEntity(o iserver.ICoordEntity, isUpdate2BeWatcher bool) {
	if e.extWatchList == nil {
		e.extWatchList = make(map[uint64]*extWatchEntity)
	}

	if _, ok := e.extWatchList[o.GetID()]; ok {
		return
	}

	inMyAOI := false
	if e.GetSpace() != nil {
		e.GetSpace().TravsalAOI(e, func(n iserver.ICoordEntity) bool {
			// 已经在AOI范围内
			if n.GetID() == o.GetID() {
				inMyAOI = true
			}
			return false
		})
	}

	// if !inMyAOI {
	// 	e.OnEntityEnterAOI(o)
	// }

	e.extWatchList[o.GetID()] = &extWatchEntity{
		entity:             o,
		isInAOI:            inMyAOI,
		isUpdate2BeWatcher: isUpdate2BeWatcher,
	}

	//这里记录下被观察者
	if bewather, ok := o.(IBeWatcher); ok {
		// 这里其实要控制 isupdate参数，这里先这样
		bewather.AddExtWatchEntity(e, isUpdate2BeWatcher)
	}
}

// 移除观察者
func (e *TinyEntity) RemoveExtWatchEntity(o iserver.ICoordEntity) {
	if e.extWatchList == nil {
		return
	}

	_, ok := e.extWatchList[o.GetID()]
	if !ok {
		return
	}

	// inMyAOI := false

	// if e.GetSpace() != nil {
	// 	e.GetSpace().TravsalAOI(e, func(n iserver.ICoordEntity) {
	// 		// 已经在AOI范围内
	// 		if n.GetID() == o.GetID() {
	// 			inMyAOI = true
	// 		}
	// 	})
	// }

	delete(e.extWatchList, o.GetID())
	//这里记录下被观察者
	if bewather, ok := o.(IBeWatcher); ok {
		bewather.RemoveExtWatchEntity(e)
	}
}

// 清空“我”在观察者的列表
func (e *TinyEntity) ClearWatcher() {
	for _, v := range e.extWatchList {
		if watcher, ok := v.entity.GetRealPtr().(IWatcher); ok {
			watcher.RemoveExtWatchEntity(e)
		}
	}
}

// AOI是否准备好，没准备好不会放到enterlist
func (e *TinyEntity) IsAOIReady() bool {
	return true
}

// 战斗包接口 根据Key获取对应初始化参数
func (this *TinyEntity) GetInitParamByKey(key string) interface{} {
	return this.GetInitParam().(iserver.IInitParam).GetVal(key)
}

// 在不在对方的AOI内
func (this *TinyEntity) ExistsAOIWeather(o iserver.ICoordEntity) bool {
	return true
}
